﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using UA.Data;
using UA.Data.Model;
using UA.Framework.Views;

namespace UA.Framework.Authorization
{
    /// <summary>
    /// 登录验证服务，提供一组用于快速进行登录验证和获取登录状态信息的 API。
    /// </summary>
    public class UAAuthentication
    {
        const string _DEFAULT_USERTOKEN_NAME = "_ua_userToken";
        private static string _defaultUrl = "~/Home/Main";
        private static string _loginUrl = "~/Home/Login";


        /// <summary>
        /// 获取当前系统用于存储身份验证票证信息的 Cookie 名称。
        /// 如果当前在 Web.config/App.config - appSettings 设置了 "UAAUTH:ticketCookieName"（注意大小写），则该属性
        ///     将取 appSettings 中指定的值，否则取 FormsAuthentication.FormsCookieName 值。
        /// </summary>
        public static string TicketCookieName
        {
            get
            {
                string value = System.Configuration.ConfigurationManager.AppSettings["UAAUTH:ticketCookieName"];
                return string.IsNullOrWhiteSpace(value) ? FormsAuthentication.FormsCookieName : value;
            }
        }

        /// <summary>
        /// 获取当前系统用于存储身份验证票证信息的 Session 项名称。
        /// 如果当前在 Web.config/App.config - appSettings 设置了 "UAAUTH:ticketSessionName"（注意大小写），则该属性
        ///     将取 appSettings 中指定的值，否则取 TicketCookieName 属性值。
        /// </summary>
        public static string TicketSessionName
        {
            get
            {
                string value = System.Configuration.ConfigurationManager.AppSettings["UAAUTH:ticketSessionName"];
                return string.IsNullOrWhiteSpace(value) ? TicketCookieName : value;
            }
        }

        /// <summary>
        /// 获取当前系统用于存储身份验证用户主体信息（UAPrincipal）的 Session 项名称。
        /// 如果当前在 Web.config/App.config - appSettings 设置了 "UAAUTH:userTokenName"（注意大小写），则该属性
        ///     将取 appSettings 中指定的值，否则取默认值 "_ua_userToken"。
        /// </summary>
        public static string TokenSessionName
        {
            get
            {
                string value = System.Configuration.ConfigurationManager.AppSettings["UAAUTH:userTokenName"];
                return string.IsNullOrWhiteSpace(value) ? _DEFAULT_USERTOKEN_NAME : value;
            }
        }



        /// <summary>
        /// 获取当前登录验证服务在没有指定重定向 URL 时 <see cref="RedirectFromLoginPage "/> 方法进行重定向操作时所使用的 URL。
        /// 如果当前在 Web.config/App.config - appSettings 设置了 "UAAUTH:defaultUrl"（注意大小写），则该属性
        ///     将取 appSettings 中指定的值，否则取 FormsAuthentication.DefaultUrl 值。
        /// </summary>
        public static string DefaultUrl
        {
            get
            {
                string value = System.Configuration.ConfigurationManager.AppSettings["UAAUTH:defaultUrl"];
                return string.IsNullOrWhiteSpace(value) ? _defaultUrl : value;
            }
        }

        /// <summary>
        /// 获取当前登录验证服务在将重定向到的登录页的 URL。
        /// 如果当前在 Web.config/App.config - appSettings 设置了 "UAAUTH:loginUrl"（注意大小写），则该属性
        ///     将取 appSettings 中指定的值，否则取 FormsAuthentication.LoginUrl 值。
        /// </summary>
        public static string LoginUrl
        {
            get
            {
                string value = System.Configuration.ConfigurationManager.AppSettings["UAAUTH:loginUrl"];
                return string.IsNullOrWhiteSpace(value) ? _loginUrl : value;
            }
        }





        /// <summary>
        /// 登录验证服务的核心 API 之一；获取一个布尔值，该值判断当前会话状态是否经过登录验证。
        /// 该属性计算时，会先后判断：
        ///     客户端 Cookie 中是否存在登录票证、
        ///     服务器 Session 中是否存在登录票证、
        ///     服务器 Session 中是否存在登录用户主体信息（UAPrincipal）、
        ///     客户端 Cookie 中的登录票证 和 服务器 Session 中的登录票证信息是否匹配（票证中记录的时间戳和用户 ID 是否相同）、
        ///     服务器 Session 中的登录用户主体信息（UAPrincipal）是否有效（数据库中存在该用户记录、且该主体信息的用户 ID 是否与 Cookie 和 Session 中的信息都相同）；
        ///  如果以上条件判断都正确，则说明当前会话状态经过了有效的登录验证，否则表示当前会话状态未经过登录验证或验证状态已过期。
        /// </summary>
        public static bool IsAuthenticated
        {
            get
            {
                FormsAuthenticationTicket ticketCookie = TicketCookie, ticketSession = TicketSession;
                UAPrincipal token = TokenSession;
                return ticketCookie != null
                    && ticketSession != null
                    && token != null
                    && token.Identity != null
                    && ticketCookie.Name == ticketSession.Name
                    && ticketCookie.UserData == ticketSession.UserData
                    && ticketCookie.IssueDate == ticketSession.IssueDate
                    //&& token.Identity.IsAuthenticated
                    && token.UserCode == ticketCookie.UserData;
            }
        }


        /// <summary>
        /// 登录验证服务的核心 API 之一；获取一个布尔值，该值判断当前请求的客户端是否有已保留的 Cookie 会话状态。
        /// 该属性计算时，会判断客户端浏览器 Cookie 中是否存在名称为 UAAuthentication.TicketCookieName 指定的 Cookie 项，并且该 Cookie 项能被正常解析为一个 FormsAuthenticationTicket 票证对象。
        /// 如果以上条件判断正确，则说明当前请求的客户端已有一个经过了登录验证（只是表示该客户端曾经成功登陆过，但并不表示当前登录状态依然有效）的 Cookie 会话状态。
        /// </summary>
        /// <remarks>
        /// 该属性值只能判断当情发送请求的客户端是否曾经成功登陆过，但并不表示当前登录状态依然有效；
        /// 如果需要判断当前会话状态是否依然具备可用的登录状态，请使用 <seealso cref="UAAuthentication.IsAuthenticated"/> 属性。
        /// </remarks>
        public static bool IsCookieAuthenticated
        {
            get
            {
                return TicketCookie != null;
            }
        }




        public static FormsAuthenticationTicket TicketCookie
        {
            get
            {
                FormsAuthenticationTicket ticket = null;
                if (HttpContext.Current == null || HttpContext.Current.Request == null || HttpContext.Current.Request.Cookies == null)
                {
                    return ticket;
                }
                HttpCookie cookie = HttpContext.Current.Request.Cookies[TicketCookieName];
                if (cookie == null)
                {
                    return ticket;
                }
                try
                {
                    ticket = FormsAuthentication.Decrypt(cookie.Value);
                }
                catch { }
                return ticket;
            }
        }

        public static FormsAuthenticationTicket TicketSession
        {
            get
            {
                FormsAuthenticationTicket ticket = null;
                if (HttpContext.Current == null || HttpContext.Current.Session == null)
                {
                    return ticket;
                }
                ticket = HttpContext.Current.Session[TicketSessionName] as FormsAuthenticationTicket;
                return ticket;
            }
        }


        public static LoginView LoginViewCookie
        {
            get
            {
                FormsAuthenticationTicket ticket = TicketCookie;
                return (ticket != null && !ticket.Expired) ? new LoginView { LoginCode = ticket.UserData, RememberMe = true } : null;
            }
        }

        public static LoginView LoginViewSession
        {
            get
            {
                FormsAuthenticationTicket ticket = TicketSession;
                return (ticket != null && !ticket.Expired) ? new LoginView { LoginCode = ticket.UserData, RememberMe = true } : null;
            }
        }


        public static UAPrincipal TokenSession
        {
            get
            {
                UAPrincipal token = null;
                if (HttpContext.Current == null || HttpContext.Current.Session == null)
                {
                    return token;
                }
                token = HttpContext.Current.Session[TokenSessionName] as UAPrincipal;
                return token;
            }
        }


        public static UAPrincipal UserToken
        {
            get
            {
                UAPrincipal token = TokenSession;
                return IsAuthenticated ? token : null;
            }
        }


        public static void RefreshUserToken()
        {
            UAPrincipal token = UserToken;
            if (token != null)
            {
                token.Refresh();
            }
            SetAuthorizeState(token);
        }



        /// <summary>
        /// 登录验证服务的核心 API 之一；验证 用户 ID 及其对应的密码是否有效。
        /// </summary>
        /// <param name="userID">用户 ID。</param>
        /// <param name="password">用户密码，明文格式。</param>
        /// <returns>如果在数据库中验证该 用户 ID 和 用户密码 匹配，则返回 true，否则返回 false。</returns>
        public static bool Authenticate(int userID, string password)
        {
            UserAccessor accessor = new UserAccessor();
            User user = accessor.GetByID(userID);
            return user != null && user.Password == password;
        }

        /// <summary>
        /// 登录验证服务的核心 API 之一；验证 用户编号 及其对应的 密码 是否有效。
        /// </summary>
        /// <param name="userCode">用户编号。</param>
        /// <param name="password">用户密码，明文格式。</param>
        /// <returns>如果在数据库中验证该 用户编号 和 用户密码 匹配，则返回 true，否则返回 false。</returns>
        public static bool Authenticate(string userCode, string password)
        {
            UserAccessor accessor = new UserAccessor();
            User user = accessor.GetByCode(userCode);
            return user != null && user.Password == password;
        }



        /// <summary>
        /// 登录验证服务的核心 API 之一；
        /// 以指定的 用户 ID 及其 用户密码 和 登录状态有效期（分钟） 进行登录验证操作。
        /// 如果登录信息验证正确，则同时设置客户端 Cookie 中的登录票证信息，并保存至服务器 Session 中以备以后进行状态校验。
        /// </summary>
        /// <param name="userID">用户 ID。</param>
        /// <param name="password">用户密码，明文格式。</param>
        /// <param name="expires">登录状态有效期（分钟）可选参数。</param>
        /// <returns>如果 用户 ID 和 用户密码 验证正确，则返回 true，否则返回 false。</returns>
        public static bool UALogin(int userID, string password, int? expires = null)
        {
            UAPrincipal principal = new UAPrincipal(userID);
            bool ret = principal.HasUser && principal.Password == password;
            if (ret)
            {
                SetAuthorizeState(principal, expires);
            }
            return ret;
        }

        /// <summary>
        /// 登录验证服务的核心 API 之一；
        /// 以指定的 用户编号 及其 用户密码 和 登录状态有效期（分钟） 进行登录验证操作。
        /// 如果登录信息验证正确，则同时设置客户端 Cookie 中的登录票证信息，并保存至服务器 Session 中以备以后进行状态校验。
        /// </summary>
        /// <param name="userCode">用户编号。</param>
        /// <param name="password">用户密码，明文格式。</param>
        /// <param name="expires">登录状态有效期（分钟）。</param>
        /// <returns>如果 用户编号 和 用户密码 验证正确，则返回 true，否则返回 false。</returns>
        public static bool UALogin(string userCode, string password, int? expires = null)
        {
            UAPrincipal principal = new UAPrincipal(userCode);
            bool ret = principal.HasUser && principal.Password == password;
            if (ret)
            {
                SetAuthorizeState(principal, expires);
            }
            return ret;
        }



        /// <summary>
        /// 登录验证服务的核心 API 之一；
        /// 登录服务中的登出操作。该操作会执行如下动作：
        ///     清除 客户端 Cookie 中的登录票证信息、
        ///     清除 服务器端 Session 中的登录票证信息、
        ///     清除 服务器端表示登录验证状态的用户主体信息（UAPrincipal）。
        /// </summary>
        public static void SignOut()
        {
            FormsAuthentication.SignOut();
            HttpContext ctx = HttpContext.Current;
            HttpCookie cookie = ctx.Request.Cookies[TicketCookieName];
            if (cookie != null)
            {
                cookie.Expires = DateTime.Now.AddDays(-1);
                ctx.Response.Cookies.Add(cookie);
            }
            ctx.Session.Remove(TicketSessionName);
            ctx.Session.Remove(TokenSessionName);
            ctx.Session.Abandon();
        }



        /// <summary>
        /// 登录验证服务的核心 API 之一；
        /// 将指定的用户主体信息设置为当前的登录用户。
        /// </summary>
        /// <param name="principal"></param>
        protected internal static void SetAuthorizeState(UAPrincipal principal)
        {
            SetAuthorizeState(principal, null);
        }


        /// <summary>
        /// 登录验证服务的核心 API 之一；
        /// 将指定的用户主体信息设置为当前的登录用户，并指定登录状态的有效期（分钟）。
        /// </summary>
        /// <param name="principal"></param>
        /// <param name="expirs"></param>
        protected internal static void SetAuthorizeState(UAPrincipal principal, int? expirs)
        {
            DateTime expiration = expirs == null ? DateTime.Now.AddMinutes(30) : DateTime.Now.AddMinutes(expirs.Value);
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, Convert.ToString(principal.UserID), DateTime.Now, expiration, false, principal.UserCode);
            string encryptTicket = FormsAuthentication.Encrypt(ticket);

            HttpCookie cookie = new HttpCookie(TicketCookieName, encryptTicket);
            if (expirs != null)
            {
                cookie.Expires = expiration;
            }

            HttpContext ctx = HttpContext.Current;
            ctx.Response.Cookies.Add(cookie);
            ctx.Session[TicketSessionName] = ticket;
            ctx.Session[TokenSessionName] = principal;
            ctx.User = principal;
        }
    }
}
